/*!
 *
 * Copyright (c) 2019, zhang jianchun.
 * Released under the MIT License.
 */


class myRTCPeerConnection extends RTCPeerConnection {
    constructor(configuration = {
        // bundlePolicy:'balanced',//指定当远程对等点与SDP BUNDLE标准不兼容时如何处理候选者的协商
        iceServers: [{//STUN和/或TURN服务器
          "url": "stun:stun.l.google.com:19302"
        }] 
    }) {
      /*
公开的免费STUN服务器有：

stun.freeswitch.org
stun.xten.com
stun.ekiga.net

免费 STUN服务器列表（亲测有效）
NO. STUN服务器 端口 有效 测试日期
0 stun:stun.l.google.com:19302
1 stun.xten.com 3478 2017/08/14
2 stun.voipbuster.com 3478 2017/08/14
3 stun.voxgratia.org 3478 2017/08/14
4 stun.sipgate.net 10000 2017/08/14
5 stun.ekiga.net 3478 2017/08/14
6 stun.ideasip.com 3478 2017/08/14
7 stun.schlund.de 3478 2017/08/14
8 stun.voiparound.com 3478 2017/08/14
9 stun.voipbuster.com 3478 2017/08/14
10 stun.voipstunt.com 3478 2017/08/14
11 numb.viagenie.ca 3478 2017/08/14
12 stun.counterpath.com 3478 2017/08/14
13 stun.1und1.de 3478 2017/08/14
14 stun.gmx.net 3478 2017/08/14
16 stun.callwithus.com 3478 2017/08/14
17 stun.counterpath.net 3478 2017/08/14
18 stun.internetcalls.com 3478 2017/08/14
19 stun.voip.aebc.com 3478 2017/08/14
20 numb.viagenie.ca 3478 2017/08/14
  "stun.services.mozilla.com"
  "stun.stunprotocol.org:3478",


      */
      // 下面的配置建立了两个ICE服务器。第一个（stun:stun.services.mozilla.com）需要验证，因此提供了用户名和密码。第二台服务器有两个URL：stun:stun.example.com和stun:stun-1.example.com。
/*      var configuration = { iceServers: [{
                                urls: "stun:stun.services.mozilla.com",
                                username: "louis@mozilla.com", 
                                credential: "webrtcdemo"
                            }, {
                                urls: [
                                        "stun:stun.example.com",
                                        "stun:stun-1.example.com"
                                ]
                            }]
      };*/
      // 避免在urls属性中指定不必要的大量URL ；连接的启动时间将大大增加。在选择要用于协商的服务器之前，将联系并试用列表中的每台服务器。
        super(configuration)

                   let offerOption= {
                voiceActivityDetection :true,//（启用语音活动检测）默认 true
                iceRestart:false,//重新启动ICE 默认 false
                // offerToReceiveAudio: 1,//设置为 发送"sendonly"或"inactive" 默认 false
                // offerToReceiveVideo: 1,// 布尔选项，用于控制是否向远程对等方提供尝试发送视频的机会,默认是仅在本地端正在发送视频时才提供接收视频，否则不提供。 默认 false
                /* SDP 
如果请求某媒体流的方向为sendonly，那么响应中对应媒体的方向必须为recvonly；
如果请求某媒体流的方向为recvonly，那么响应中对应媒体的方向必须为sendonly；
如果请求某媒体流的方向为sendrecv，那么响应中对应媒体的方向可以为sendrecv/sendonly/recvonly/inactive中的一种；
如果请求某媒体流的方向为inactive，那么响应中对应媒体的方向必须为inactive；
                */
            },
        // this.offer
        // this.answer
        /**\
            A 呼叫 B

            A 创建 offer 信息后，
              存储本地 offer 描述，
              再将 offer 其发送给 B。
            B 收到 offer 后，
              存储远端 offer 描述；
              然后又创建 answer 信息，
              存储本地 answer 描述，
              再返回 answer 给 A
            A 拿到 answer 后，
              设置远端 answer 描述。

         \**/

        /**\
           A 呼叫 B

            A:
            
                let A_RTC = new myRTCPeerConnection()
                peer.addStream(stream); // 添加本地流 获 
                let A_offer= A_RTC.createOffer(offerOption)//创建 offer 信息后 offerOption 如果提供尝试发送视频的机会 会触发 onicecandidate
                A_RTC.setLocalDescription(A_offer) //存储本地 offer 描述，
                goto_B(A_offer) //发送  再将 offer 其发送给 B。
                ongoto_A=(answer)=>{ //收到 
                 // answer =new RTCSessionDescription(answer)//不建议使用构造函数。
                    A_RTC.setRemoteDescription(answer)
                }

           B:
               ongoto_B=(A_offer)=>{ //收到
                   let B_RTC = new myRTCPeerConnection()
                  var desc =new RTCSessionDescription(A_offer)
                   B_RTC.setRemoteDescription(desc)
                   let answer = B_RTC.createAnswer()
                   B_RTC.setLocalDescription(answer)
                    goto_A(answer) //发送
               }

        \**/
    }
}

/*\
属性

RTCPeerConnection.iceConnectionState 只读
返回一个RTCIceConnectionState类型的结构体，这个结构体描述了连接的ICE连接状态。当这个状态的值改变时，这个对象会触发一个iceconnectionstatechange 事件。状态可能存在的值如下：
"new": ICE服务器正在收集地址或正在等待远端的candidates(这两种情况可能同时存在)。
"checking": ICE服务器找到了远端的candidates（连接候选者）,这些candidates至少有一个，同时ICE服务器在检测这些candidates，尽管它可能还没有找到连接。此刻，ICE服务器可能仍在收集candidates（连接候选者）。
"connected": ICE服务器已经找到了一条可用的适合所有组件的连接，但它仍然在测试更多的远端candidate以提供更好的连接。同时，ICE服务器可能仍在收集candidates。
"completed": ICE服务器已经找到了一条可用的连接，并不再测试远端candidates。
"failed": ICE服务器已经检测了所有的远端candidates，但并没有找到可用的。对一些组件适用的连接可能已经被找到。
"disconnected": 至少一个组件的活跃度检查失败了，这可能是由糟糕的网络环境造成的一个短期状态，它可以被它自身所修复。
"closed": ICE服务器已经关闭，并不再响应请求。

RTCPeerConnection.iceGatheringState 只读
返回一个iceGatheringState类型的结构体，它描述了这条连接的ICE收集状态。该状态可能取以下的值：
"new": 对象刚刚被创建,还没有网络化。
"gathering": ICE引擎正在为连接收集candidates(连接候选者)。
"complete": 引擎已经完成了candidates收集。但像添加一个新的接口或者一个新的turn服务器这些事件会导致状态回到"gathering"。

RTCPeerConnection.localDescription 只读
返回一个 RTCSessionDescription ，它描述了这条连接的本地端的会话控制（用户会话所需的属性以及配置信息）。如果本地的会话控制还没有被设置，它的值就会是null。

RTCPeerConnection.peerIdentity 只读
返回一个RTCIdentityAssertion,它由一组信息构成，包括一个域名（idp）以及一个名称（name），它们代表了这条连接的远端机器的身份识别信息。如果远端机器还没有被设置以及校验，这个属性会返回一个null值。一旦被设置，它不能被一般方法改变。

RTCPeerConnection.remoteDescription 只读
返回一个 RTCSessionDescription 它描述了这条连接的远端机器的会话控制，如果远端机器还未被设置，它的值会是null。

RTCPeerConnection.signalingState 只读
返回一个RTC通信状态的结构体，这个结构体描述了本地连接的通信状态。这个 状态描述了一个定义连接配置的SDP offer。它包含了下列信息，与MediaStream 类型本地相关的对象的描述，媒体流编码方式或RTP和  RTCP协议的选项 ，以及被ICE服务器收集到的candidates(连接候选者)。当RTCPeerConnection.signalingState的值改变时，对象上的signalingstatechange事件会被触发。 它可能取下列的值：
"stable": 没有SDP offer/answer正在被交换，连接仍然处于初始化状态。
"have-local-offer": 这条连接的本地端机器已经本地应用了一个SDP offer。
"have-remote-offer": 这条连接的远端机器已经本地应用了一个SDP offer。
"have-local-pranswer": 一个来自远端的SDP offer已经被应用，同时一个SDP pranswer在本地被应用。
"have-remote-pranswer": 一个本地的SDP offer被应用，同时一个SDP pranswer在远端被应用。
"closed": 连接被关闭。
\*/

/*\
事件处理器
RTCPeerConnection.onaddstream  ontrack
是收到addstream 事件时调用的事件处理器。 Such an event is 当MediaStream 被远端机器添加到这条连接时，该事件会被触发。 当调用RTCPeerConnection.setRemoteDescription()方法时，这个事件就会被立即触发，它不会等待SDP协商的结果。

RTCPeerConnection.ondatachannel
是收到datachannel 事件时调用的事件处理器。 当一个 RTCDataChannel 被添加到连接时，这个事件被触发。

RTCPeerConnection.onicecandidate
是收到 icecandidate 事件时调用的事件处理器.。当一个 RTCICECandidate 对象被添加时，这个事件被触发。

RTCPeerConnection.oniceconnectionstatechange
是收到iceconnectionstatechange事件时调用的事件处理器 。 当iceConnectionState 改变时，这个事件被触发。

RTCPeerConnection.onidentityresult
是收到 identityresult事件时调用的事件处理器。 当通过getIdentityAssertion()生成身份断言, 或在生成一个answer或一个offer的过程中，这个事件被触发。

RTCPeerConnection.onidpassertionerror
是收到 idpassertionerror 事件时调用的事件处理器。当生成一个身份断言时，如果关联的身份提供者（idP）遇到一个错误，这个事件就会被触发。

RTCPeerConnection.onidpvalidationerror
是收到 idpvalidationerror 事件时调用的事件处理器。当检查 一个身份断言时，如果关联的身份提供者（idP）遇到一个错误，这个事件就会被触发。

RTCPeerConnection.onnegotiationneeded
是收到negotiationneeded 事件时调用的事件处理器， 浏览器发送该事件以告知在将来某一时刻需要协商。

RTCPeerConnection.onpeeridentity
是收到peeridentity 事件时调用的事件处理器, 当一条连接的peer identify被设置以及校验后，该事件被触发

RTCPeerConnection.onremovestream
是收到removestream 事件时调用的事件处理器，当一条MediaStream 从连接上移除时，该事件被触发。

RTCPeerConnection.onsignalingstatechange
是收到signalingstatechange 事件时调用的事件处理器, 当signalingState的值发生改变时，该事件被触发。

\*/

/*\
方法
RTCPeerConnection()
RTCPeerConnection的初始化函数，通过 new RTCPeerConnection()初始化一个RTCPeerConnection实例。

RTCPeerConnection.createOffer()
生成一个offer，它是一个带有特定的配置信息寻找远端匹配机器（peer）的请求。这个方法的前两个参数分别是方法调用成功以及失败的回调函数，可选的第三个参数是用户对视频流以及音频流的定制选项（一个对象）。

RTCPeerConnection.createAnswer()
在协调一条连接中的两端offer/answers时，根据从远端发来的offer生成一个answer。这个方法的前两个参数分别是方法调用成功以及失败时的回调函数，可选的第三个参数是生成的answer的可供选项。

RTCPeerConnection.setLocalDescription()
改变与连接相关的本地描述。这个描述定义了连接的属性，例如：连接的编码方式。连接会受到它的改变的影响，而且连接必须能同时支持新的以及旧的描述。这个方法可以接收三个参数，一个RTCSessionDescription 对象包含设置信息，还有两个回调函数，它们分别是方法调用成功以及失败的回调函数。

RTCPeerConnection.setRemoteDescription()
改变与连接相关的远端描述。这个描述定义了连接的属性，例如：连接的编码方式。连接会受到它的改变的影响，而且连接必须能同时支持新的以及旧的描述。这个方法可以接收三个参数，一个RTCSessionDescription 对象包含设置信息，还有两个回调函数，它们分别是方法调用成功以及失败的回调函数。

RTCPeerConnection.updateIce()
更新ICE服务器时调用的方法。

RTCPeerConnection.addIceCandidate()
添加iceCandidate时调用的方法。

RTCPeerConnection.getConfiguration()
获取配置信息时调用的方法。

RTCPeerConnection.getLocalStreams()
返回连接的本地媒体流数组。这个数组可能是空数组。

RTCPeerConnection.getRemoteStreams()
返回连接的远端媒体流数组。这个数组可能是空数组。

RTCPeerConnection.getStreamById()
返回连接中与所给id匹配的媒体流 MediaStream，如果没有匹配项，返回null。


RTCPeerConnection.addStream()   用   addTrack
添加一个媒体流 MediaStream作为本地音频或视频源。如果本地端与远端协调已经发生了，那么需要一个新的媒体流，这样远端才可以使用它。

RTCPeerConnection.removeStream() removeTrack
将一个作为本地音频或视频源的媒体流 MediaStream移除。如果本地端与远端协调已经发生了，那么需要一个新的媒体流，这样远端才可以停止使用它。

RTCPeerConnection.close()
关闭一个RTCPeerConnection实例所调用的方法。

RTCPeerConnection.createDataChannel()
在一条连接上建立一个新的RTCDataChannel（用于数据发送）。这个方法把一个数据对象作为参数，数据对象中包含必要的配置信息。

RTCPeerConnection.createDTMFSender()
创建一个新的与特殊的MediaStreamTrack相关的RTCDTMFSender，可以在连接上发送DTMF手机信号。

RTCPeerConnection.getStats()
生成一个新的RTCStatsReport，它包含连接相关的统计信息。

RTCPeerConnection.setIdentityProvider()
根据所给的三个参数设置身份提供者（IdP)，这三个参数是它的名称，通信所使用的协议（可选），以及一个可选的用户名。只有当一个断言被需要时，这个IdP才会被使用。

RTCPeerConnection.getIdentityAssertion()
初始化身份断言的收集，只有当signalingState的值不为"closed"时，它才有效。它自动完成，在需求发生前调用它是最好的选择。
\*/